package com.baidu.iit.pxp.converter;

import com.baidu.iit.pxp.constants.BpmnXMLConstants;
import com.baidu.iit.pxp.model.*;
import com.baidu.iit.pxp.model.Process;
import com.baidu.iit.pxp.util.BpmnXMLUtil;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.xml.stream.XMLStreamReader;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * User: huangweili
 * Date: 14-4-25
 * Time: 下午12:34
 */
public abstract class BaseBpmnXMLConverter {

    protected static final Logger LOGGER = LoggerFactory.getLogger(BaseBpmnXMLConverter.class);

    protected static final List<ExtensionAttribute> defaultElementAttributes = Arrays.asList(
            new ExtensionAttribute(BpmnXMLConstants.ATTRIBUTE_ID),
            new ExtensionAttribute(BpmnXMLConstants.ATTRIBUTE_NAME)
    );

    protected static final List<ExtensionAttribute> defaultActivityAttributes = Arrays.asList(
            new ExtensionAttribute(BpmnXMLConstants.ACTIVITI_EXTENSIONS_NAMESPACE, BpmnXMLConstants.ATTRIBUTE_ACTIVITY_ASYNCHRONOUS),
            new ExtensionAttribute(BpmnXMLConstants.ACTIVITI_EXTENSIONS_NAMESPACE, BpmnXMLConstants.ATTRIBUTE_ACTIVITY_EXCLUSIVE),
            new ExtensionAttribute(BpmnXMLConstants.ATTRIBUTE_DEFAULT),
            new ExtensionAttribute(BpmnXMLConstants.ACTIVITI_EXTENSIONS_NAMESPACE, BpmnXMLConstants.ATTRIBUTE_ACTIVITY_ISFORCOMPENSATION)
    );

    public void convertToBpmnModel(XMLStreamReader xtr, BpmnModel model, Process activeProcess,
                                   List<SubProcess> activeSubProcessList) throws Exception {

        String elementId = xtr.getAttributeValue(null, BpmnXMLConstants.ATTRIBUTE_ID);
        String elementName = xtr.getAttributeValue(null, BpmnXMLConstants.ATTRIBUTE_NAME);
        boolean async = parseAsync(xtr);
        boolean notExclusive = parseNotExclusive(xtr);
        String defaultFlow = xtr.getAttributeValue(null, BpmnXMLConstants.ATTRIBUTE_DEFAULT);
        boolean isForCompensation = parseForCompensation(xtr);

        BaseElement parsedElement = convertXMLToElement(xtr, model);
        if (parsedElement instanceof FlowElement) {

            FlowElement currentFlowElement = (FlowElement) parsedElement;
            currentFlowElement.setId(elementId);
            currentFlowElement.setName(elementName);

            if (currentFlowElement instanceof Activity) {

                Activity activity = (Activity) currentFlowElement;
                activity.setAsynchronous(async);
                activity.setNotExclusive(notExclusive);
                activity.setForCompensation(isForCompensation);
                if (StringUtils.isNotEmpty(defaultFlow)) {
                    activity.setDefaultFlow(defaultFlow);
                }
            }

            if (currentFlowElement instanceof Gateway) {
                if (StringUtils.isNotEmpty(defaultFlow)) {
                    ((Gateway) currentFlowElement).setDefaultFlow(defaultFlow);
                }
            }

            if (currentFlowElement instanceof DataObject) {
                if (activeSubProcessList.size() > 0) {
                    activeSubProcessList.get(activeSubProcessList.size() - 1).getDataObjects().add((ValuedDataObject) parsedElement);
                } else {
                    activeProcess.getDataObjects().add((ValuedDataObject) parsedElement);
                }
            }

            if (activeSubProcessList.size() > 0) {
                activeSubProcessList.get(activeSubProcessList.size() - 1).addFlowElement(currentFlowElement);
            } else {
                activeProcess.addFlowElement(currentFlowElement);
            }
        }
    }


    protected abstract Class<? extends BaseElement> getBpmnElementType();

    protected abstract BaseElement convertXMLToElement(XMLStreamReader xtr, BpmnModel model) throws Exception;

    protected abstract String getXMLElementName();


    protected void parseChildElements(String elementName, BaseElement parentElement, BpmnModel model, XMLStreamReader xtr) throws Exception {
        parseChildElements(elementName, parentElement, null, model, xtr);
    }

    protected void parseChildElements(String elementName, BaseElement parentElement, Map<String, BaseChildElementParser> additionalParsers,
                                      BpmnModel model, XMLStreamReader xtr) throws Exception {

        Map<String, BaseChildElementParser> childParsers = new HashMap<String, BaseChildElementParser>();
        if (additionalParsers != null) {
            childParsers.putAll(additionalParsers);
        }
        BpmnXMLUtil.parseChildElements(elementName, parentElement, xtr, childParsers, model);
    }

    protected ExtensionElement parseExtensionElement(XMLStreamReader xtr) throws Exception {
        ExtensionElement extensionElement = new ExtensionElement();
        extensionElement.setName(xtr.getLocalName());
        if (StringUtils.isNotEmpty(xtr.getNamespaceURI())) {
            extensionElement.setNamespace(xtr.getNamespaceURI());
        }
        if (StringUtils.isNotEmpty(xtr.getPrefix())) {
            extensionElement.setNamespacePrefix(xtr.getPrefix());
        }

        BpmnXMLUtil.addCustomAttributes(xtr, extensionElement, defaultElementAttributes);

        boolean readyWithExtensionElement = false;
        while (readyWithExtensionElement == false && xtr.hasNext()) {
            xtr.next();
            if (xtr.isCharacters()) {
                if (StringUtils.isNotEmpty(xtr.getText().trim())) {
                    extensionElement.setElementText(xtr.getText().trim());
                }
            } else if (xtr.isStartElement()) {
                ExtensionElement childExtensionElement = parseExtensionElement(xtr);
                extensionElement.addChildElement(childExtensionElement);
            } else if (xtr.isEndElement() && extensionElement.getName().equalsIgnoreCase(xtr.getLocalName())) {
                readyWithExtensionElement = true;
            }
        }
        return extensionElement;
    }

    protected boolean parseAsync(XMLStreamReader xtr) {
        boolean async = false;
        String asyncString = xtr.getAttributeValue(BpmnXMLConstants.ACTIVITI_EXTENSIONS_NAMESPACE, BpmnXMLConstants.ATTRIBUTE_ACTIVITY_ASYNCHRONOUS);
        if (BpmnXMLConstants.ATTRIBUTE_VALUE_TRUE.equalsIgnoreCase(asyncString)) {
            async = true;
        }
        return async;
    }

    protected boolean parseNotExclusive(XMLStreamReader xtr) {
        boolean notExclusive = false;
        String exclusiveString = xtr.getAttributeValue(BpmnXMLConstants.ACTIVITI_EXTENSIONS_NAMESPACE, BpmnXMLConstants.ATTRIBUTE_ACTIVITY_EXCLUSIVE);
        if (BpmnXMLConstants.ATTRIBUTE_VALUE_FALSE.equalsIgnoreCase(exclusiveString)) {
            notExclusive = true;
        }
        return notExclusive;
    }

    protected boolean parseForCompensation(XMLStreamReader xtr) {
        boolean isForCompensation = false;
        String compensationString = xtr.getAttributeValue(null, BpmnXMLConstants.ATTRIBUTE_ACTIVITY_ISFORCOMPENSATION);
        if (BpmnXMLConstants.ATTRIBUTE_VALUE_TRUE.equalsIgnoreCase(compensationString)) {
            isForCompensation = true;
        }
        return isForCompensation;
    }

    protected List<String> parseDelimitedList(String expression) {
        return BpmnXMLUtil.parseDelimitedList(expression);
    }

    private boolean isInSubProcess(List<SubProcess> subProcessList) {
        if (subProcessList.size() > 1) {
            return true;
        } else {
            return false;
        }
    }


    protected String convertToDelimitedString(List<String> stringList) {
        return BpmnXMLUtil.convertToDelimitedString(stringList);
    }


}
